import arrayPrototype from "./array";
import Dep from "./dep";

class Observer {
  constructor(data) {
    // 给 Observer 实例，挂载一个依赖 Dep 的实例，为了给数组属性变化用来调用视图更新函数（updateComponent, 本质还是调用 render 函数）
    this.dep = new Dep();

    // __ob__ 表示数据是已经被观测的对象
    // data.__ob__ = this; // 这样会导致内存溢出，需要把 data 上挂载的 __ob__ 设置为不可以枚举即可
    // 设置 data.__ob__ 不可枚举的，解决方案：
    Object.defineProperty(data, '__ob__', {
      value: this,
      enumerable: false, // 设置为不可枚举的
    })

    // 如果 data 是数组, 不需要对索引也观测（vue2消耗性能的太大），重写数组的方法（push,pop,shift,unshift,reverse,sort,splice）实现数据响应式
    // vue2 对数组的长度和索引是没有观测的，所以不要通过索引和修改数据长度来实现响应式的数据变化
    if (Array.isArray(data)) {
      // 观察数组，重写 7 个数组的原生方法 push,pop,shift,unshift,reverse,sort,splice
      data.__proto__ = arrayPrototype;
      // 如果数组中存放是是对象，也观测它，变成响应式
      this.observeArray(data);
    } else {
      // 遍历观测对象
      this.walk(data);
    }
  }

  // 观测数组中的对象
  observeArray(data) {
    data.forEach(item => observe(item));
  }

  // 观测对象
  walk(data) {
    const keys = Object.keys(data);
    keys.forEach(key => {
      defineReactive(data, key, data[key]);
    })
  }
}

// 数组的依赖收集
function dependArray(value) {
  value.forEach(v => {
    v.__ob__ && v.__ob__.dep.depend(); // 依赖收集
    if (Array.isArray(v)) {
      // 递归对数组做依赖收集
      dependArray(v);
    }
  });
}

// Vue2 的响应式原理的核心方法 defineReactive
function defineReactive(data, key, value) {
  // 实例化依赖收集对象
  const dep = new Dep();

  let childOb = observe(value); // 递归深度遍历属性，进行观测（vue2消耗性能的一个地方）
  Object.defineProperty(data, key, {
    get() {
      // console.log('get')
      // 每个属性都对应自己的 渲染 Watcher
      if (Dep.target) {
        // 存放对应的 Watcher，对象的依赖收集
        dep.depend();
        if (childOb) {
          // 存放对应的 Watcher，数组的依赖收集
          childOb.dep.depend();
          // 如果数组中还有数组，递归做依赖收集
          if (Array.isArray(value)) {
            dependArray(value);
          }
        }
      }
      return value;
    },
    set(newValue) {
      // console.log('set')

      if (newValue === value) return;
      // 如果用户通过 vm 设置属性，也进行观测,实现响应式
      childOb = observe(newValue);
      value = newValue;

      // 属性变化,通知对应依赖的 watcher 执行更新视图操作
      dep.notify();
    }
  });
}

export function observe(data) {
  // 不是对象，或者为 null / undefined 就返回
  if (typeof data !== "object" || data == null) return;

  // __ob__ 标识是否已经被观测过
  if (data.__ob__) return data;


  return new Observer(data);
}